// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include <map>

#include "../../include/pdfwindow/PDFWindow.h"
#include "../../include/pdfwindow/PWL_Wnd.h"
#include "../../include/pdfwindow/PWL_Utils.h"
#include "../../include/pdfwindow/PWL_ScrollBar.h"

/* -------------------------- CPWL_Timer -------------------------- */

static std::map<int32_t, CPWL_Timer*>& GetPWLTimeMap()
{
  // Leak the object at shutdown.
  static auto timeMap = new std::map<int32_t, CPWL_Timer*>;
  return *timeMap;
}

CPWL_Timer::CPWL_Timer(CPWL_TimerHandler* pAttached, IFX_SystemHandler* pSystemHandler) :
    m_nTimerID(0),
    m_pAttached(pAttached),
    m_pSystemHandler(pSystemHandler)
{
    ASSERT(m_pAttached != NULL);
    ASSERT(m_pSystemHandler != NULL);
}

CPWL_Timer::~CPWL_Timer()
{
    KillPWLTimer();
}

int32_t CPWL_Timer::SetPWLTimer(int32_t nElapse)
{
    if (m_nTimerID != 0)
        KillPWLTimer();
    m_nTimerID = m_pSystemHandler->SetTimer(nElapse, TimerProc);

    GetPWLTimeMap()[m_nTimerID] = this;
    return m_nTimerID;
}

void CPWL_Timer::KillPWLTimer()
{
    if (m_nTimerID == 0)
        return;

    m_pSystemHandler->KillTimer(m_nTimerID);
    GetPWLTimeMap().erase(m_nTimerID);
    m_nTimerID = 0;
}

void CPWL_Timer::TimerProc(int32_t idEvent)
{
    auto it = GetPWLTimeMap().find(idEvent);
    if (it == GetPWLTimeMap().end())
        return;

    CPWL_Timer* pTimer = it->second;
    if (pTimer->m_pAttached)
        pTimer->m_pAttached->TimerProc();
}

/* -------------------------- CPWL_TimerHandler -------------------------- */

CPWL_TimerHandler::CPWL_TimerHandler() : m_pTimer(NULL)
{
}

CPWL_TimerHandler::~CPWL_TimerHandler()
{
    delete m_pTimer;
}

void CPWL_TimerHandler::BeginTimer(int32_t nElapse)
{
    if (!m_pTimer)
        m_pTimer = new CPWL_Timer(this, GetSystemHandler());

    if (m_pTimer)
        m_pTimer->SetPWLTimer(nElapse);
}

void CPWL_TimerHandler::EndTimer()
{
    if (m_pTimer)
        m_pTimer->KillPWLTimer();
}

void CPWL_TimerHandler::TimerProc()
{
}

/* --------------------------- CPWL_MsgControl ---------------------------- */

class CPWL_MsgControl
{
    friend class CPWL_Wnd;

public:
    CPWL_MsgControl(CPWL_Wnd * pWnd)
    {
//      PWL_TRACE("new CPWL_MsgControl\n");
        m_pCreatedWnd = pWnd;
        Default();
    }

    ~CPWL_MsgControl()
    {
//      PWL_TRACE("~CPWL_MsgControl\n");
        Default();
    }

    void Default()
    {
        m_aMousePath.RemoveAll();
        m_aKeyboardPath.RemoveAll();
        m_pMainMouseWnd = NULL;
        m_pMainKeyboardWnd = NULL;
    }

    FX_BOOL IsWndCreated(const CPWL_Wnd * pWnd) const
    {
        return m_pCreatedWnd == pWnd;
    }

    FX_BOOL IsMainCaptureMouse(const CPWL_Wnd * pWnd) const
    {
        return pWnd == m_pMainMouseWnd;
    }

    FX_BOOL IsWndCaptureMouse(const CPWL_Wnd * pWnd) const
    {
        if (pWnd)
            for( int32_t i=0,sz=m_aMousePath.GetSize(); i<sz; i++)
                if (m_aMousePath.GetAt(i) == pWnd)
                    return TRUE;

        return FALSE;
    }

    FX_BOOL IsMainCaptureKeyboard(const CPWL_Wnd * pWnd) const
    {
        return pWnd == m_pMainKeyboardWnd;
    }


    FX_BOOL IsWndCaptureKeyboard(const CPWL_Wnd * pWnd) const
    {
        if (pWnd)
            for( int32_t i=0,sz=m_aKeyboardPath.GetSize(); i<sz; i++)
                if (m_aKeyboardPath.GetAt(i) == pWnd)
                    return TRUE;

        return FALSE;
    }

    void SetFocus(CPWL_Wnd * pWnd)
    {
        m_aKeyboardPath.RemoveAll();

        if (pWnd)
        {
            m_pMainKeyboardWnd = pWnd;

            CPWL_Wnd * pParent = pWnd;
            while (pParent)
            {
                m_aKeyboardPath.Add(pParent);
                pParent = pParent->GetParentWindow();
            }

            pWnd->OnSetFocus();
        }
    }

    void KillFocus()
    {
        if (m_aKeyboardPath.GetSize() > 0)
            if (CPWL_Wnd* pWnd = m_aKeyboardPath.GetAt(0))
                pWnd->OnKillFocus();

        m_pMainKeyboardWnd = NULL;
        m_aKeyboardPath.RemoveAll();
    }

    void SetCapture(CPWL_Wnd * pWnd)
    {
        m_aMousePath.RemoveAll();

        if (pWnd)
        {
            m_pMainMouseWnd = pWnd;

            CPWL_Wnd * pParent = pWnd;
            while (pParent)
            {
                m_aMousePath.Add(pParent);
                pParent = pParent->GetParentWindow();
            }
        }
    }

    void ReleaseCapture()
    {
        m_pMainMouseWnd = NULL;
        m_aMousePath.RemoveAll();
    }

private:
    CFX_ArrayTemplate<CPWL_Wnd*>    m_aMousePath;
    CFX_ArrayTemplate<CPWL_Wnd*>    m_aKeyboardPath;
    CPWL_Wnd*                       m_pCreatedWnd;
    CPWL_Wnd*                       m_pMainMouseWnd;
    CPWL_Wnd*                       m_pMainKeyboardWnd;
};

/* --------------------------- CPWL_Wnd ---------------------------- */

CPWL_Wnd::CPWL_Wnd() :
    m_pVScrollBar(NULL),
    m_rcWindow(),
    m_rcClip(),
    m_bCreated(FALSE),
    m_bVisible(FALSE),
    m_bNotifying(FALSE),
    m_bEnabled(TRUE)
{
}

CPWL_Wnd::~CPWL_Wnd()
{
    ASSERT(m_bCreated == FALSE);
}

CFX_ByteString CPWL_Wnd::GetClassName() const
{
    return "CPWL_Wnd";
}

void CPWL_Wnd::Create(const PWL_CREATEPARAM & cp)
{
    if (!IsValid())
    {
        m_sPrivateParam = cp;

        OnCreate(m_sPrivateParam);

        m_sPrivateParam.rcRectWnd.Normalize();
        m_rcWindow = m_sPrivateParam.rcRectWnd;
        m_rcClip = CPWL_Utils::InflateRect(m_rcWindow,1.0f);

        CreateMsgControl();

        if (m_sPrivateParam.pParentWnd)
            m_sPrivateParam.pParentWnd->OnNotify(this, PNM_ADDCHILD);

        PWL_CREATEPARAM ccp = m_sPrivateParam;

        ccp.dwFlags &= 0xFFFF0000L; //remove sub styles
        ccp.mtChild = CPDF_Matrix(1,0,0,1,0,0);

        CreateScrollBar(ccp);
        CreateChildWnd(ccp);

        m_bVisible = HasFlag(PWS_VISIBLE);

        OnCreated();

        RePosChildWnd();
        m_bCreated = TRUE;
    }
}

void CPWL_Wnd::OnCreate(PWL_CREATEPARAM & cp)
{
}

void CPWL_Wnd::OnCreated()
{
}

void CPWL_Wnd::OnDestroy()
{
}

void CPWL_Wnd::Destroy()
{
    KillFocus();

    OnDestroy();

    if (m_bCreated)
    {
        for (int32_t i = m_aChildren.GetSize()-1; i >= 0; i --)
        {
            if (CPWL_Wnd * pChild = m_aChildren[i])
            {
                pChild->Destroy();
                delete pChild;
                pChild = NULL;
            }
        }

        if (m_sPrivateParam.pParentWnd)
            m_sPrivateParam.pParentWnd->OnNotify(this, PNM_REMOVECHILD);
        m_bCreated = FALSE;
    }

    DestroyMsgControl();

    FXSYS_memset(&m_sPrivateParam, 0, sizeof(PWL_CREATEPARAM));
    m_aChildren.RemoveAll();
    m_pVScrollBar = NULL;
}

void CPWL_Wnd::Move(const CPDF_Rect & rcNew, FX_BOOL bReset,FX_BOOL bRefresh)
{
    if (IsValid())
    {
        CPDF_Rect rcOld = GetWindowRect();

        m_rcWindow = rcNew;
        m_rcWindow.Normalize();

        if (rcOld.left != rcNew.left || rcOld.right != rcNew.right ||
            rcOld.top != rcNew.top || rcOld.bottom != rcNew.bottom)
        {
            if (bReset)
            {
                RePosChildWnd();
            }

        }
        if (bRefresh)
        {
            InvalidateRectMove(rcOld,rcNew);
        }

        m_sPrivateParam.rcRectWnd = m_rcWindow;
    }
}

void  CPWL_Wnd::InvalidateRectMove(const CPDF_Rect & rcOld, const CPDF_Rect & rcNew)
{
    CPDF_Rect rcUnion = rcOld;
    rcUnion.Union(rcNew);

    InvalidateRect(&rcUnion);
}

void CPWL_Wnd::GetAppearanceStream(CFX_ByteString & sAppStream)
{
    if (IsValid())
    {
        CFX_ByteTextBuf sTextBuf;
        GetAppearanceStream(sTextBuf);
        sAppStream += sTextBuf.GetByteString();
    }
}

void CPWL_Wnd::GetAppearanceStream(CFX_ByteTextBuf & sAppStream)
{
    if (IsValid() && IsVisible())
    {
        GetThisAppearanceStream(sAppStream);
        GetChildAppearanceStream(sAppStream);
    }
}

//if don't set,Get default apperance stream
void CPWL_Wnd::GetThisAppearanceStream(CFX_ByteTextBuf & sAppStream)
{
    CPDF_Rect rectWnd = GetWindowRect();
    if (!rectWnd.IsEmpty()) {
        CFX_ByteTextBuf sThis;

        if (HasFlag(PWS_BACKGROUND))
            sThis << CPWL_Utils::GetRectFillAppStream(rectWnd, GetBackgroundColor());

        if (HasFlag(PWS_BORDER)) {
            sThis << CPWL_Utils::GetBorderAppStream(
                rectWnd,
                (FX_FLOAT)GetBorderWidth(),
                GetBorderColor(),
                GetBorderLeftTopColor(GetBorderStyle()),
                GetBorderRightBottomColor(GetBorderStyle()),
                GetBorderStyle(),
                GetBorderDash());
        }

        sAppStream << sThis;
    }
}

void CPWL_Wnd::GetChildAppearanceStream(CFX_ByteTextBuf & sAppStream)
{
    for (int32_t i=0,sz=m_aChildren.GetSize(); i<sz; i++)
    {
        if (CPWL_Wnd * pChild = m_aChildren.GetAt(i))
        {
            pChild->GetAppearanceStream(sAppStream);
        }
    }
}

void CPWL_Wnd::DrawAppearance(CFX_RenderDevice* pDevice, CPDF_Matrix* pUser2Device)
{
    if (IsValid() && IsVisible())
    {
        DrawThisAppearance(pDevice,pUser2Device);
        DrawChildAppearance(pDevice,pUser2Device);
    }
}

void CPWL_Wnd::DrawThisAppearance(CFX_RenderDevice* pDevice, CPDF_Matrix* pUser2Device)
{
    CPDF_Rect rectWnd = GetWindowRect();
    if (!rectWnd.IsEmpty())
    {
        if (HasFlag(PWS_BACKGROUND))
        {
            CPDF_Rect rcClient = CPWL_Utils::DeflateRect(rectWnd,(FX_FLOAT)(GetBorderWidth()+GetInnerBorderWidth()));
            CPWL_Utils::DrawFillRect(pDevice,pUser2Device,rcClient, GetBackgroundColor(), GetTransparency());
        }

        if (HasFlag(PWS_BORDER))
            CPWL_Utils::DrawBorder(pDevice,
                                pUser2Device,
                                rectWnd,
                                (FX_FLOAT)GetBorderWidth(),
                                GetBorderColor(),
                                GetBorderLeftTopColor(GetBorderStyle()),
                                GetBorderRightBottomColor(GetBorderStyle()),
                                GetBorderStyle(),
                                GetBorderDash(),
                                GetTransparency());
    }
}

void CPWL_Wnd::DrawChildAppearance(CFX_RenderDevice* pDevice, CPDF_Matrix* pUser2Device)
{
    for (int32_t i=0,sz=m_aChildren.GetSize(); i<sz; i++)
    {
        if (CPWL_Wnd * pChild = m_aChildren.GetAt(i))
        {
            CPDF_Matrix mt = pChild->GetChildMatrix();
            if (mt.IsIdentity())
            {
                pChild->DrawAppearance(pDevice,pUser2Device);
            }
            else
            {
                mt.Concat(*pUser2Device);
                pChild->DrawAppearance(pDevice,&mt);
            }
        }
    }
}

void CPWL_Wnd::InvalidateRect(CPDF_Rect* pRect)
{
    if (IsValid())
    {
        CPDF_Rect rcRefresh = pRect ? *pRect : GetWindowRect();

        if (!HasFlag(PWS_NOREFRESHCLIP))
        {
            CPDF_Rect rcClip = GetClipRect();
            if (!rcClip.IsEmpty())
            {
                rcRefresh.Intersect(rcClip);
            }
        }

        FX_RECT rcWin = PWLtoWnd(rcRefresh);
        rcWin.left -= PWL_INVALIDATE_INFLATE;
        rcWin.top -= PWL_INVALIDATE_INFLATE;
        rcWin.right += PWL_INVALIDATE_INFLATE;
        rcWin.bottom += PWL_INVALIDATE_INFLATE;

        if (IFX_SystemHandler* pSH = GetSystemHandler())
        {
            if (FX_HWND hWnd = GetAttachedHWnd())
            {
                pSH->InvalidateRect(hWnd, rcWin);
            }
        }
    }
}

#define PWL_IMPLEMENT_KEY_METHOD(key_method_name)\
FX_BOOL CPWL_Wnd::key_method_name(FX_WORD nChar, FX_DWORD nFlag)\
{\
    if (IsValid() && IsVisible() && IsEnabled())\
    {\
        if (IsWndCaptureKeyboard(this))\
        {\
            for (int32_t i=0,sz=m_aChildren.GetSize(); i<sz; i++)\
            {\
                if (CPWL_Wnd * pChild = m_aChildren.GetAt(i))\
                {\
                    if (IsWndCaptureKeyboard(pChild))\
                    {\
                        return pChild->key_method_name(nChar,nFlag);\
                    }\
                }\
            }\
        }\
    }\
    return FALSE;\
}

#define PWL_IMPLEMENT_MOUSE_METHOD(mouse_method_name)\
FX_BOOL CPWL_Wnd::mouse_method_name(const CPDF_Point & point, FX_DWORD nFlag)\
{\
    if (IsValid() && IsVisible() && IsEnabled())\
    {\
        if (IsWndCaptureMouse(this))\
        {\
            for (int32_t i=0,sz=m_aChildren.GetSize(); i<sz; i++)\
            {\
                if (CPWL_Wnd * pChild = m_aChildren.GetAt(i))\
                {\
                    if (IsWndCaptureMouse(pChild))\
                    {\
                        return pChild->mouse_method_name(pChild->ParentToChild(point),nFlag);\
                    }\
                }\
            }\
            SetCursor();\
        }\
        else\
        {\
            for (int32_t i=0,sz=m_aChildren.GetSize(); i<sz; i++)\
            {\
                if (CPWL_Wnd * pChild = m_aChildren.GetAt(i))\
                {\
                    if (pChild->WndHitTest(pChild->ParentToChild(point)))\
                    {\
                        return pChild->mouse_method_name(pChild->ParentToChild(point),nFlag);\
                    }\
                }\
            }\
            if (WndHitTest(point))\
                SetCursor();\
        }\
    }\
    return FALSE;\
}

PWL_IMPLEMENT_KEY_METHOD(OnKeyDown)
PWL_IMPLEMENT_KEY_METHOD(OnKeyUp)
PWL_IMPLEMENT_KEY_METHOD(OnChar)

PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonDblClk)
PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonDown)
PWL_IMPLEMENT_MOUSE_METHOD(OnLButtonUp)
PWL_IMPLEMENT_MOUSE_METHOD(OnMButtonDblClk)
PWL_IMPLEMENT_MOUSE_METHOD(OnMButtonDown)
PWL_IMPLEMENT_MOUSE_METHOD(OnMButtonUp)
PWL_IMPLEMENT_MOUSE_METHOD(OnRButtonDown)
PWL_IMPLEMENT_MOUSE_METHOD(OnRButtonUp)
PWL_IMPLEMENT_MOUSE_METHOD(OnMouseMove)

FX_BOOL CPWL_Wnd::OnMouseWheel(short zDelta, const CPDF_Point & point, FX_DWORD nFlag)
{
    if (IsValid() && IsVisible() && IsEnabled())
    {
        SetCursor();
        if (IsWndCaptureKeyboard(this))
        {
            for (int32_t i=0,sz=m_aChildren.GetSize(); i<sz; i++)
            {
                if (CPWL_Wnd * pChild = m_aChildren.GetAt(i))
                {
                    if (IsWndCaptureKeyboard(pChild))
                    {
                        return pChild->OnMouseWheel(zDelta,pChild->ParentToChild(point), nFlag);
                    }
                }
            }
        }
    }
    return FALSE;
}

void CPWL_Wnd::AddChild(CPWL_Wnd * pWnd)
{
    m_aChildren.Add(pWnd);
}

void CPWL_Wnd::RemoveChild(CPWL_Wnd * pWnd)
{
    for (int32_t i = m_aChildren.GetSize()-1; i >= 0; i --)
    {
        if (CPWL_Wnd* pChild = m_aChildren.GetAt(i))
        {
            if (pChild == pWnd)
            {
                m_aChildren.RemoveAt(i);
                break;
            }
        }
    }
}

void CPWL_Wnd::OnNotify(CPWL_Wnd* pWnd, FX_DWORD msg, intptr_t wParam, intptr_t lParam)
{
    switch (msg)
    {
    case PNM_ADDCHILD:
        AddChild(pWnd);
        break;
    case PNM_REMOVECHILD:
        RemoveChild(pWnd);
        break;
    default:
        break;
    }
}

FX_BOOL CPWL_Wnd::IsValid() const
{
    return m_bCreated;
}

PWL_CREATEPARAM CPWL_Wnd::GetCreationParam() const
{
    return m_sPrivateParam;
}

CPWL_Wnd* CPWL_Wnd::GetParentWindow() const
{
    return m_sPrivateParam.pParentWnd;
}

CPDF_Rect CPWL_Wnd::GetOriginWindowRect() const
{
    return m_sPrivateParam.rcRectWnd;
}

CPDF_Rect CPWL_Wnd::GetWindowRect() const
{
    return m_rcWindow;
}

CPDF_Rect CPWL_Wnd::GetClientRect() const
{
    CPDF_Rect rcWindow = GetWindowRect();
    CPDF_Rect rcClient = CPWL_Utils::DeflateRect(rcWindow,(FX_FLOAT)(GetBorderWidth()+GetInnerBorderWidth()));
    if (CPWL_ScrollBar* pVSB = GetVScrollBar())
        rcClient.right -= pVSB->GetScrollBarWidth();

    rcClient.Normalize();
    return rcWindow.Contains(rcClient) ? rcClient : CPDF_Rect();
}

CPDF_Point CPWL_Wnd::GetCenterPoint() const
{
    CPDF_Rect rcClient = GetClientRect();
    return CPDF_Point((rcClient.left + rcClient.right) * 0.5f,
                      (rcClient.top + rcClient.bottom) * 0.5f);
}

CPDF_Rect CPWL_Wnd::GetClientCenterSquare() const
{
    return CPWL_Utils::GetCenterSquare(GetClientRect());
}

CPDF_Rect CPWL_Wnd::GetWindowCenterSquare() const
{
    return CPWL_Utils::GetCenterSquare(CPWL_Utils::DeflateRect(GetWindowRect(),0.1f));
}

FX_BOOL CPWL_Wnd::HasFlag(FX_DWORD dwFlags) const
{
    return (m_sPrivateParam.dwFlags & dwFlags) != 0;
}

void CPWL_Wnd::RemoveFlag(FX_DWORD dwFlags)
{
    m_sPrivateParam.dwFlags &= ~dwFlags;
}

void CPWL_Wnd::AddFlag(FX_DWORD dwFlags)
{
    m_sPrivateParam.dwFlags |= dwFlags;
}

CPWL_Color CPWL_Wnd::GetBackgroundColor() const
{
    return m_sPrivateParam.sBackgroundColor;
}

void CPWL_Wnd::SetBackgroundColor(const CPWL_Color & color)
{
    m_sPrivateParam.sBackgroundColor = color;
}

void CPWL_Wnd::SetTextColor(const CPWL_Color & color)
{
    m_sPrivateParam.sTextColor = color;
}

void CPWL_Wnd::SetTextStrokeColor(const CPWL_Color & color)
{
    m_sPrivateParam.sTextStrokeColor = color;
}

CPWL_Color CPWL_Wnd::GetTextColor() const
{
    return m_sPrivateParam.sTextColor;
}

CPWL_Color CPWL_Wnd::GetTextStrokeColor() const
{
    return m_sPrivateParam.sTextStrokeColor;
}

int32_t CPWL_Wnd::GetBorderStyle() const
{
    return m_sPrivateParam.nBorderStyle;
}

void CPWL_Wnd::SetBorderStyle(int32_t nBorderStyle)
{
    if (HasFlag(PWS_BORDER))
        m_sPrivateParam.nBorderStyle = nBorderStyle;
}

int32_t CPWL_Wnd::GetBorderWidth() const
{
    if (HasFlag(PWS_BORDER))
        return m_sPrivateParam.dwBorderWidth;

    return 0;
}

int32_t CPWL_Wnd::GetInnerBorderWidth() const
{
    /*
    switch (GetBorderStyle())
    {
    case PBS_BEVELED:
    case PBS_INSET:
        return GetBorderWidth() / 2;
    }
    */
    return 0;
}

void CPWL_Wnd::SetBorderWidth(int32_t nBorderWidth)
{
    if (HasFlag(PWS_BORDER))
        m_sPrivateParam.dwBorderWidth = nBorderWidth;
}

CPWL_Color CPWL_Wnd::GetBorderColor() const
{
    if (HasFlag(PWS_BORDER))
        return m_sPrivateParam.sBorderColor;

    return CPWL_Color();
}

void CPWL_Wnd::SetBorderColor(const CPWL_Color & color)
{
    if (HasFlag(PWS_BORDER))
        m_sPrivateParam.sBorderColor = color;
}

CPWL_Dash CPWL_Wnd::GetBorderDash() const
{
    return m_sPrivateParam.sDash;
}

void* CPWL_Wnd::GetAttachedData() const
{
    return m_sPrivateParam.pAttachedData;
}

void CPWL_Wnd::SetBorderDash(const CPWL_Dash & sDash)
{
    if (HasFlag(PWS_BORDER))
        m_sPrivateParam.sDash = sDash;
}

CPWL_ScrollBar* CPWL_Wnd::GetVScrollBar() const
{
    if (HasFlag(PWS_VSCROLL))
        return m_pVScrollBar;

    return NULL;
}

void CPWL_Wnd::CreateScrollBar(const PWL_CREATEPARAM & cp)
{
    CreateVScrollBar(cp);
}

void CPWL_Wnd::CreateVScrollBar(const PWL_CREATEPARAM & cp)
{
    if (!m_pVScrollBar && HasFlag(PWS_VSCROLL))
    {
        PWL_CREATEPARAM scp = cp;

        //flags
        scp.dwFlags = PWS_CHILD| PWS_BACKGROUND | PWS_AUTOTRANSPARENT | PWS_NOREFRESHCLIP;

        scp.pParentWnd = this;
        scp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR;
        scp.eCursorType = FXCT_ARROW;
        scp.nTransparency = PWL_SCROLLBAR_TRANSPARANCY;

        if ((m_pVScrollBar = new CPWL_ScrollBar(SBT_VSCROLL)))
            m_pVScrollBar->Create(scp);
    }
}

void CPWL_Wnd::SetCapture()
{
    if (CPWL_MsgControl * pMsgCtrl = GetMsgControl())
        pMsgCtrl->SetCapture(this);
}

void CPWL_Wnd::ReleaseCapture()
{
    for (int32_t i=0,sz=m_aChildren.GetSize(); i<sz; i++)
        if (CPWL_Wnd* pChild = m_aChildren.GetAt(i))
            pChild->ReleaseCapture();

    if (CPWL_MsgControl * pMsgCtrl = GetMsgControl())
        pMsgCtrl->ReleaseCapture();
}

void CPWL_Wnd::SetFocus()
{
    if (CPWL_MsgControl * pMsgCtrl = GetMsgControl())
    {
        if (!pMsgCtrl->IsMainCaptureKeyboard(this))
            pMsgCtrl->KillFocus();
        pMsgCtrl->SetFocus(this);
    }
}

void CPWL_Wnd::KillFocus()
{
    if (CPWL_MsgControl * pMsgCtrl = GetMsgControl())
    {
        if (pMsgCtrl->IsWndCaptureKeyboard(this))
            pMsgCtrl->KillFocus();
    }
}

void CPWL_Wnd::OnSetFocus()
{
}

void CPWL_Wnd::OnKillFocus()
{
}

FX_BOOL CPWL_Wnd::WndHitTest(const CPDF_Point & point) const
{
    return IsValid() && IsVisible() && GetWindowRect().Contains(point.x,point.y);
}

FX_BOOL CPWL_Wnd::ClientHitTest(const CPDF_Point & point) const
{
    return IsValid() && IsVisible() && GetClientRect().Contains(point.x,point.y);
}

const CPWL_Wnd* CPWL_Wnd::GetRootWnd() const
{
    if (m_sPrivateParam.pParentWnd)
        return m_sPrivateParam.pParentWnd->GetRootWnd();

    return this;
}

void CPWL_Wnd::SetVisible(FX_BOOL bVisible)
{
    if (IsValid())
    {
        for (int32_t i=0,sz=m_aChildren.GetSize(); i<sz; i++)
        {
            if (CPWL_Wnd* pChild = m_aChildren.GetAt(i))
            {
                pChild->SetVisible(bVisible);
            }
        }

        if (bVisible != m_bVisible)
        {
            m_bVisible = bVisible;
            RePosChildWnd();
            InvalidateRect();
        }
    }
}

void CPWL_Wnd::SetClipRect(const CPDF_Rect & rect)
{
    m_rcClip = rect;
    m_rcClip.Normalize();
}

CPDF_Rect CPWL_Wnd::GetClipRect() const
{
    return m_rcClip;
}

FX_BOOL CPWL_Wnd::IsReadOnly() const
{
    return HasFlag(PWS_READONLY);
}

void CPWL_Wnd::RePosChildWnd()
{
    CPDF_Rect rcContent = CPWL_Utils::DeflateRect(GetWindowRect(),(FX_FLOAT)(GetBorderWidth()+GetInnerBorderWidth()));

    CPWL_ScrollBar * pVSB = GetVScrollBar();

    CPDF_Rect rcVScroll = CPDF_Rect(rcContent.right - PWL_SCROLLBAR_WIDTH,
                            rcContent.bottom,
                            rcContent.right-1.0f,
                            rcContent.top);

    if (pVSB) pVSB->Move(rcVScroll,TRUE,FALSE);
}

void CPWL_Wnd::CreateChildWnd(const PWL_CREATEPARAM & cp)
{
}

void CPWL_Wnd::SetCursor()
{
    if (IsValid())
    {
        if (IFX_SystemHandler* pSH = GetSystemHandler())
        {
            int32_t nCursorType = GetCreationParam().eCursorType;
            pSH->SetCursor(nCursorType);
        }
    }
}

void CPWL_Wnd::CreateMsgControl()
{
    if (!m_sPrivateParam.pMsgControl)
        m_sPrivateParam.pMsgControl = new CPWL_MsgControl(this);
}

void CPWL_Wnd::DestroyMsgControl()
{
    if (CPWL_MsgControl* pMsgControl = GetMsgControl())
        if (pMsgControl->IsWndCreated(this))
            delete pMsgControl;
}

CPWL_MsgControl* CPWL_Wnd::GetMsgControl() const
{
    return m_sPrivateParam.pMsgControl;
}

FX_BOOL CPWL_Wnd::IsCaptureMouse() const
{
    return IsWndCaptureMouse(this);
}

FX_BOOL CPWL_Wnd::IsWndCaptureMouse(const CPWL_Wnd * pWnd) const
{
    if (CPWL_MsgControl * pCtrl = GetMsgControl())
        return pCtrl->IsWndCaptureMouse(pWnd);

    return FALSE;
}

FX_BOOL CPWL_Wnd::IsWndCaptureKeyboard(const CPWL_Wnd * pWnd) const
{
    if (CPWL_MsgControl * pCtrl = GetMsgControl())
        return pCtrl->IsWndCaptureKeyboard(pWnd);

    return FALSE;
}

FX_BOOL CPWL_Wnd::IsFocused() const
{
    if (CPWL_MsgControl * pCtrl = GetMsgControl())
        return pCtrl->IsMainCaptureKeyboard(this);

    return FALSE;
}

CPDF_Rect CPWL_Wnd::GetFocusRect() const
{
    return CPWL_Utils::InflateRect(GetWindowRect(),1);
}

FX_FLOAT CPWL_Wnd::GetFontSize() const
{
    return m_sPrivateParam.fFontSize;
}

void CPWL_Wnd::SetFontSize(FX_FLOAT fFontSize)
{
    m_sPrivateParam.fFontSize = fFontSize;
}

IFX_SystemHandler* CPWL_Wnd::GetSystemHandler() const
{
    return m_sPrivateParam.pSystemHandler;
}

IPWL_FocusHandler* CPWL_Wnd::GetFocusHandler() const
{
    return m_sPrivateParam.pFocusHandler;
}

IPWL_Provider* CPWL_Wnd::GetProvider() const
{
    return m_sPrivateParam.pProvider;
}

IFX_Edit_FontMap* CPWL_Wnd::GetFontMap() const
{
    return m_sPrivateParam.pFontMap;
}

CPWL_Color CPWL_Wnd::GetBorderLeftTopColor(int32_t nBorderStyle) const
{
    CPWL_Color color;

    switch (nBorderStyle)
    {
        case PBS_SOLID:
            break;
        case PBS_DASH:
            break;
        case PBS_BEVELED:
            color = CPWL_Color(COLORTYPE_GRAY,1);
            break;
        case PBS_INSET:
            color = CPWL_Color(COLORTYPE_GRAY,0.5f);
            break;
        case PBS_UNDERLINED:
            break;
    }

    return color;
}

CPWL_Color CPWL_Wnd::GetBorderRightBottomColor(int32_t nBorderStyle) const
{
    CPWL_Color color;

    switch (nBorderStyle)
    {
        case PBS_SOLID:
            break;
        case PBS_DASH:
            break;
        case PBS_BEVELED:
            color = CPWL_Utils::DevideColor(GetBackgroundColor(),2);
            break;
        case PBS_INSET:
            color = CPWL_Color(COLORTYPE_GRAY,0.75f);
            break;
        case PBS_UNDERLINED:
            break;
    }

    return color;
}

/* ----------------------------------------------------------------- */

int32_t CPWL_Wnd::GetTransparency()
{
    return m_sPrivateParam.nTransparency;
}

void CPWL_Wnd::SetTransparency(int32_t nTransparency)
{
    for (int32_t i=0,sz=m_aChildren.GetSize(); i<sz; i++)
    {
        if (CPWL_Wnd* pChild = m_aChildren.GetAt(i))
        {
            pChild->SetTransparency(nTransparency);
        }
    }

    m_sPrivateParam.nTransparency = nTransparency;
}

CPDF_Matrix CPWL_Wnd::GetWindowMatrix() const
{
    CPDF_Matrix mt = GetChildToRoot();

    if (IPWL_Provider* pProvider = GetProvider())
    {
        mt.Concat(pProvider->GetWindowMatrix(GetAttachedData()));
        return mt;
    }

    return mt;
}

void CPWL_Wnd::PWLtoWnd(const CPDF_Point& point, int32_t& x, int32_t& y) const
{
    CPDF_Matrix mt = GetWindowMatrix();
    CPDF_Point pt = point;
    mt.Transform(pt.x,pt.y);
    x = (int32_t)(pt.x+0.5);
    y = (int32_t)(pt.y+0.5);
}

FX_RECT CPWL_Wnd::PWLtoWnd(const CPDF_Rect & rect) const
{
    CPDF_Rect rcTemp = rect;
    CPDF_Matrix mt = GetWindowMatrix();
    mt.TransformRect(rcTemp);
    return FX_RECT((int32_t)(rcTemp.left+0.5), (int32_t)(rcTemp.bottom+0.5), (int32_t)(rcTemp.right+0.5), (int32_t)(rcTemp.top+0.5));
}

FX_HWND CPWL_Wnd::GetAttachedHWnd() const
{
    return m_sPrivateParam.hAttachedWnd;
}

CPDF_Point CPWL_Wnd::ChildToParent(const CPDF_Point& point) const
{
    CPDF_Matrix mt = GetChildMatrix();
    if (mt.IsIdentity())
        return point;

    CPDF_Point pt = point;
    mt.Transform(pt.x,pt.y);
    return pt;
}

CPDF_Rect CPWL_Wnd::ChildToParent(const CPDF_Rect& rect) const
{
    CPDF_Matrix mt = GetChildMatrix();
    if (mt.IsIdentity())
        return rect;

    CPDF_Rect rc = rect;
    mt.TransformRect(rc);
    return rc;
}

CPDF_Point CPWL_Wnd::ParentToChild(const CPDF_Point& point) const
{
    CPDF_Matrix mt = GetChildMatrix();
    if (mt.IsIdentity())
        return point;

    mt.SetReverse(mt);
    CPDF_Point pt = point;
    mt.Transform(pt.x,pt.y);
    return pt;
}

CPDF_Rect CPWL_Wnd::ParentToChild(const CPDF_Rect& rect) const
{
    CPDF_Matrix mt = GetChildMatrix();
    if (mt.IsIdentity())
        return rect;

    mt.SetReverse(mt);
    CPDF_Rect rc = rect;
    mt.TransformRect(rc);
    return rc;
}

CPDF_Matrix CPWL_Wnd::GetChildToRoot() const
{
    CPDF_Matrix mt(1, 0, 0, 1, 0, 0);
    if (HasFlag(PWS_CHILD))
    {
        const CPWL_Wnd* pParent = this;
        while (pParent)
        {
            mt.Concat(pParent->GetChildMatrix());
            pParent = pParent->GetParentWindow();
        }
    }
    return mt;
}

CPDF_Matrix CPWL_Wnd::GetChildMatrix() const
{
    if (HasFlag(PWS_CHILD))
        return m_sPrivateParam.mtChild;

    return CPDF_Matrix(1,0,0,1,0,0);
}

void CPWL_Wnd::SetChildMatrix(const CPDF_Matrix& mt)
{
    m_sPrivateParam.mtChild = mt;
}

const CPWL_Wnd* CPWL_Wnd::GetFocused() const
{
    if (CPWL_MsgControl * pMsgCtrl = GetMsgControl())
    {
        return pMsgCtrl->m_pMainKeyboardWnd;
    }

    return NULL;
}

void CPWL_Wnd::EnableWindow(FX_BOOL bEnable)
{
    if (m_bEnabled != bEnable)
    {
        for (int32_t i=0,sz=m_aChildren.GetSize(); i<sz; i++)
        {
            if (CPWL_Wnd* pChild = m_aChildren.GetAt(i))
            {
                pChild->EnableWindow(bEnable);
            }
        }

        m_bEnabled = bEnable;

        if (bEnable)
            OnEnabled();
        else
            OnDisabled();
    }
}

FX_BOOL CPWL_Wnd::IsEnabled()
{
    return m_bEnabled;
}

void CPWL_Wnd::OnEnabled()
{
}

void CPWL_Wnd::OnDisabled()
{
}

FX_BOOL CPWL_Wnd::IsCTRLpressed(FX_DWORD nFlag) const
{
    if (IFX_SystemHandler* pSystemHandler = GetSystemHandler())
    {
        return pSystemHandler->IsCTRLKeyDown(nFlag);
    }

    return FALSE;
}

FX_BOOL CPWL_Wnd::IsSHIFTpressed(FX_DWORD nFlag) const
{
    if (IFX_SystemHandler* pSystemHandler = GetSystemHandler())
    {
        return pSystemHandler->IsSHIFTKeyDown(nFlag);
    }

    return FALSE;
}

FX_BOOL CPWL_Wnd::IsALTpressed(FX_DWORD nFlag) const
{
    if (IFX_SystemHandler* pSystemHandler = GetSystemHandler())
    {
        return pSystemHandler->IsALTKeyDown(nFlag);
    }

    return FALSE;
}

FX_BOOL CPWL_Wnd::IsINSERTpressed(FX_DWORD nFlag) const
{
    if (IFX_SystemHandler* pSystemHandler = GetSystemHandler())
    {
        return pSystemHandler->IsINSERTKeyDown(nFlag);
    }

    return FALSE;
}
